﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Linq;
namespace TBFramework
{

    public class StandardMethod : StandardReturnValueMethod
    {

    }

    public class StandardReturnValueMethod : StandrdUIMethod
    {
        /// <summary>
        /// 获取资源文件夹下切分图片。
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Sprite[] ReturnResourcesSprites(string path)
        {
            Sprite[] sprites = Resources.LoadAll<Sprite>(path);
            if (sprites.Length > 0)
            {
                // 创建导出文件夹

                foreach (Sprite sprite in sprites)
                {
                    print(sprite);
                }
            }
            return sprites;
        }

    }

    public class StandrdUIMethod : StandrdControlMethod
    {

        /// <summary>
        /// 让指定的 UI 跟随鼠标移动。
        /// 该方法会将传入的 RectTransform 组件的位置设置为当前鼠标在屏幕上的位置。
        /// </summary>
        /// <param name="uiRectTransform">需要跟随鼠标移动的 UI 的 RectTransform 组件。</param>
        public void UIFollowMouse(RectTransform uiRectTransform, OffsetState offsetState, RawImage MiniMap, Camera uiCamera, Canvas canvas)
        {
            if (uiRectTransform != null)
            {

                Vector2 localPoint;
                if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
                {
                    RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), Input.mousePosition, null, out localPoint);
                }
                else
                {
                    RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), Input.mousePosition, uiCamera, out localPoint);
                }

                switch (offsetState)
                {
                    case OffsetState.LEFT:
                        localPoint.x += 5;
                        localPoint.y -= (uiRectTransform.rect.height + 5);
                        break;
                    case OffsetState.Right:
                        localPoint.x -= (uiRectTransform.rect.width + 5);
                        localPoint.y -= (uiRectTransform.rect.height + 5);
                        break;
                    case OffsetState.TOP:
                        //mousePosition.x -= (uiRectTransform.rect.width / 2 + 5)* uiRectTransform.root.lossyScale.x;
                        localPoint.y -= (uiRectTransform.rect.height + 20);
                        break;
                }

                uiRectTransform.anchoredPosition = localPoint;

            }
        }
        /// <summary>
        /// 打字效果，样式最后显示。
        /// </summary>
        /// 使用方法
        /// string tmpText = text.text;
        /// TypeTextWithRichText(text, tmpText, 10f);
        /// <param name="textComponent">显示文字组件</param>
        /// <param name="fullText">显示的内容</param>
        /// <param name="duration">显示时长</param>
        /// <returns></returns>

        public static async Task TypeTextWithRichText(Text textComponent, string fullText, float duration)
        {
            // 存储富文本标签及其位置
            List<(int index, string tag)> tags = new List<(int index, string tag)>();
            string plainText = "";
            int tagOffset = 0;

            // 提取富文本标签和普通文本
            var matches = Regex.Matches(fullText, @"<[^>]*>|[^<]+");
            foreach (Match match in matches)
            {
                if (match.Value.StartsWith("<"))
                {
                    tags.Add((match.Index - tagOffset, match.Value));
                    tagOffset += match.Length;
                }
                else
                {
                    plainText += match.Value;
                }
            }

            string currentText = "";
            float charDuration = duration / plainText.Length;

            // 逐个显示普通文本字符
            for (int i = 0; i < plainText.Length; i++)
            {
                currentText += plainText[i];
                textComponent.text = currentText;
                await Task.Delay((int)(charDuration * 1000));
            }

            // 全部普通文本显示完成后，添加富文本标签
            foreach (var tag in tags.OrderByDescending(t => t.index))
            {
                currentText = currentText.Insert(tag.index, tag.tag);
            }
            textComponent.text = currentText;
        }

        /// <summary>
        /// 为指定的游戏对象添加 UI 拖拽限制组件，并设置标题高度。
        /// 该方法会在传入的游戏对象上添加 <see cref="UIObjectsAddDrag"/> 组件，
        /// 并将其 <c>titleHight</c> 属性设置为指定的值。
        /// </summary>
        /// <param name="dragUiObj">需要添加 UI 拖拽限制功能的游戏对象。</param>
        /// <param name="titleHight">标题的高度，用于控制拖拽触发区域，默认值为 40。</param>
        public void UIObjectsAddDrag()
        {
            foreach (var item in StandardProperty.Instance.draggableUIObjects)
            {
                item.draggableUI.AddComponent<UIDragClamp>().titleHight = item.titleHight;
                item.draggableUI.GetComponent<RectTransform>().anchoredPosition = new Vector2(item.offset.x, -item.offset.y);
            }
        }
        /// <summary>
        /// 对 UI 元素进行定位。根据 3D 对象的世界位置计算其在屏幕上的位置，
        /// 并根据 UI 父对象的不同，设置 UI 元素的锚点位置。
        /// </summary>
        public void UIPositioning()
        {
            if (StandardProperty.Instance.minMaxWindows.isOpen)
            {
                StandardProperty.Instance.minMaxWindows.posUI.gameObject.SetActive(true);
                Vector3 worldPosition = StandardProperty.Instance.minMaxWindows.target3Dobj.position;
                Vector3 screenPosition = StandardProperty.Instance.minMaxWindows.uiCamera.WorldToScreenPoint(worldPosition);
                if (StandardProperty.Instance.minMaxWindows.windows.parent.name == "Canvas")
                {
                    var x = StandardProperty.Instance.minMaxWindows.canvas.sizeDelta.x;
                    var y = StandardProperty.Instance.minMaxWindows.canvas.sizeDelta.y;
                    StandardProperty.Instance.minMaxWindows.posUI.anchoredPosition = new Vector3(screenPosition.x * x / 1920f, screenPosition.y * y / 1080f, 0);
                }
                else
                {
                    var x = StandardProperty.Instance.minMaxWindows.windows.parent.GetComponent<RectTransform>().sizeDelta.x;
                    var y = StandardProperty.Instance.minMaxWindows.windows.parent.GetComponent<RectTransform>().sizeDelta.y - 40;
                    var w = StandardProperty.Instance.minMaxWindows.canvas.sizeDelta.x;
                    var h = StandardProperty.Instance.minMaxWindows.canvas.sizeDelta.y;
                    var p = StandardProperty.Instance.minMaxWindows.windows.parent.GetComponent<RectTransform>().anchoredPosition;
                    var px = p.x;
                    var py = p.y - StandardProperty.Instance.minMaxWindows.windows.parent.GetComponent<RectTransform>().sizeDelta.y;
                    StandardProperty.Instance.minMaxWindows.posUI.anchoredPosition = new Vector2(screenPosition.x * x / 1920f + px, (screenPosition.y * y / 1080f + h + py));//, StandardProperty.Instance.minMaxWindows.posUI.anchoredPosition.z
                }
                StandardProperty.Instance.minMaxWindows.posUI.transform.SetAsLastSibling();
            }
            else
            {
                StandardProperty.Instance.minMaxWindows.miniBorder.gameObject.SetActive(false);
                StandardProperty.Instance.minMaxWindows.posUI.gameObject.SetActive(false);
            }
        }
        /// <summary>
        /// 切换最小化和最大化窗口的状态。
        /// 根据传入的布尔值参数，决定是将窗口切换到最小化状态还是恢复到原始状态。
        /// </summary>
        /// <param name="isChange">如果为 true，则将窗口切换到最小化状态；如果为 false，则将窗口恢复到原始状态。</param>
        public void ChangeMinMaxWin(bool isChange)
        {

            if (isChange)
            {
                StandardProperty.Instance.minMaxWindows.miniBorder.gameObject.SetActive(true);
                StandardProperty.Instance.minMaxWindows.windows.SetParent(StandardProperty.Instance.minMaxWindows.miniBorder);
                StandardProperty.Instance.minMaxWindows.windows.anchorMin = Vector2.zero; // 左下角锚点
                StandardProperty.Instance.minMaxWindows.windows.anchorMax = Vector2.one;  // 右上角锚点
                StandardProperty.Instance.minMaxWindows.windows.offsetMin = Vector2.zero; // Left=0, Bottom=0
                StandardProperty.Instance.minMaxWindows.windows.offsetMax = new Vector2(0, -40f); // Right=0, Top=0
                StandardProperty.Instance.minMaxWindows.windows.SetSiblingIndex(StandardProperty.Instance.minMaxWindows.windows.parent.childCount - 2);
            }
            else
            {
                StandardProperty.Instance.minMaxWindows.windows.SetParent(StandardProperty.Instance.minMaxWindows.miniBorder.transform.parent);
                StandardProperty.Instance.minMaxWindows.miniBorder.gameObject.SetActive(false);
                StandardProperty.Instance.minMaxWindows.windows.anchorMin = Vector2.zero; // 左下角锚点
                StandardProperty.Instance.minMaxWindows.windows.anchorMax = Vector2.one;  // 右上角锚点
                StandardProperty.Instance.minMaxWindows.windows.offsetMin = Vector2.zero; // Left=0, Bottom=0
                StandardProperty.Instance.minMaxWindows.windows.offsetMax = Vector2.zero; // Right=0, Top=0
                StandardProperty.Instance.minMaxWindows.windows.SetAsFirstSibling();
            }
        }
    }
    public class StandrdControlMethod : MonoBehaviour
    {
        /// <summary>
        /// 处理鼠标点击射线检测，并对检测到的物体执行一系列操作。
        /// 该方法为虚方法，允许派生类进行重写。
        /// </summary>
        /// <param name="camera">用于发射射线的相机。</param>
        /// <param name="distance">射线的检测距离。</param>
        /// <param name="layerMask">射线检测的图层掩码，用于过滤检测的对象。</param>
        /// <param name="actions">检测到物体后要执行的操作列表，每个操作接收一个 Transform 类型的参数。</param>
        public virtual void MainCameraRayControl(Camera camera, float distance, LayerMask layerMask, List<Action<Transform>> actions)
        {
            if (Input.GetMouseButtonDown(0))
            {
                Ray ray = camera.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, distance, layerMask))
                {
                    if (actions != null)
                    {
                        foreach (var action in actions)
                        {
                            action?.Invoke(hit.collider.gameObject.transform);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 基于 UI 相机和小地图执行射线检测。当鼠标在小地图上点击时，发射一条射线并打印击中物体的名称。
        /// </summary>
        /// <param name="MiniMap">小地图的 RawImage 组件，用于获取鼠标在小地图上的位置。</param>
        /// <param name="uiCamera">用于发射射线的 UI 相机。</param>
        /// <param name="distance">射线的检测距离。</param>
        /// <param name="layerMask">射线检测的图层掩码，用于过滤检测的对象。</param>
        /// <param name="actions">检测到物体后要执行的操作列表，每个操作接收一个 Transform 类型的参数。</param>
        public void UiCameraRayControl(RawImage MiniMap, Camera uiCamera, float distance, LayerMask layerMask, List<Action<Transform>> actions, Camera uiCameraControl, Canvas canvas)
        {
            if (Input.GetMouseButtonDown(0))
            {
                Vector2 localPoint;
                if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
                {
                    RectTransformUtility.ScreenPointToLocalPointInRectangle(MiniMap.rectTransform, Input.mousePosition, null, out localPoint);
                }
                else
                {
                    RectTransformUtility.ScreenPointToLocalPointInRectangle(MiniMap.rectTransform, Input.mousePosition, uiCamera, out localPoint);
                }
                Vector2 uv = new Vector2((localPoint.x + MiniMap.rectTransform.rect.width / 2) / MiniMap.rectTransform.rect.width, (localPoint.y + MiniMap.rectTransform.rect.height / 2) / MiniMap.rectTransform.rect.height);
                Vector3 viewportPos = new Vector3(uv.x, uv.y, uiCamera.nearClipPlane);
                Ray ray = uiCameraControl.ViewportPointToRay(viewportPos);

                Debug.DrawRay(uiCameraControl.transform.position, new Vector3(ray.direction.x, ray.direction.y, ray.direction.z) * distance, Color.red, 1000f);


                RaycastHit hit;
                if (Physics.Raycast(new Ray(uiCameraControl.transform.position, ray.direction), out hit, distance, layerMask))
                {

                    if (actions != null)
                    {
                        foreach (var action in actions)
                        {
                            action?.Invoke(hit.collider.gameObject.transform);
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// 相机控制类
    /// </summary>
    public class CameraControl : MonoBehaviour
    {

        public Transform camTargetTransf;
        public float distance = 1f;
        public float distanceDecay = 0.5f;
        public float xSpeed = 250.0f;
        public float ySpeed = 120.0f;
        public float zSpeed = 250.0f;
        public float pSpeed = 1.0f;
        public float yMinLimit = -20;
        public float yMaxLimit = 80;
        public float zMinLimit = 5;
        public float zMaxLimit = 20;
        public bool isFlex = false;
        public float flexTime = 1.0f;

        private float flexRatio = 0.0f;
        private float flexRatioZ = 0.0f;
        private float flexRatioP = 0.0f;
        private float xFlex = 0.0f;
        private float yFlex = 0.0f;
        private float zFlex = 0.0f;
        private float _xFlex = 0.0f;
        private float _yFlex = 0.0f;


        public float x = 0f;
        public float y = 0f;

        private float _x = 0.0f;
        private float _y = 0.0f;
        public Transform cameraTarget;

        void Awake()
        {

            GameObject go = new GameObject("Camera_Target");
            if (camTargetTransf)
            {
                go.transform.position = camTargetTransf.position;
                go.transform.eulerAngles = camTargetTransf.eulerAngles;
            }
            else
            {
                print("Please set camTargetTransf !");
            }
            cameraTarget = go.transform;

        }



        void Start()
        {
            if (camTargetTransf != null)
            {
                transform.LookAt(camTargetTransf.position);
                //print (transform.eulerAngles+"______2");
                Vector3 angles = transform.eulerAngles;
                //print (transform.eulerAngles+"______3");
                x = angles.x;
                y = angles.y;
            }
        }
        // Update is called once per frame
        void Update()
        {
            SetFlexRatio(flexTime);
        }



        void LateUpdate()
        {

            float curDistanceRatio = Vector3.Distance(transform.position, cameraTarget.position);
            curDistanceRatio *= distanceDecay;

            if (Input.GetAxis("Mouse ScrollWheel") != 0.0f)
            {
                zFlex = Input.GetAxis("Mouse ScrollWheel") * zSpeed * -0.01f * curDistanceRatio;
                distance += zFlex;
                flexRatioZ = 1.0f;
            }
            else if (isFlex)
            {
                distance += zFlex * flexRatioZ;
            }
            distance = Mathf.Clamp(distance, zMinLimit, zMaxLimit);
            //左键
            if (Input.GetMouseButton(1))
            {
                xFlex = Input.GetAxis("Mouse X") * xSpeed * 0.01f;
                yFlex = Input.GetAxis("Mouse Y") * ySpeed * 0.01f;
                y += xFlex;
                x -= yFlex;
                flexRatio = 1.0f;
            }
            else if (isFlex)
            {
                y += xFlex * flexRatio;
                x -= yFlex * flexRatio;
            }
            x = ClampAngle(x, yMinLimit, yMaxLimit);
            Quaternion rotation = Quaternion.Euler(x, y, 0);
            transform.rotation = rotation;
            Vector3 position = rotation * new Vector3(0.0f, 0.0f, -distance) + cameraTarget.position;
            transform.position = position;

            //中键
            if (Input.GetMouseButton(2))
            {
                _xFlex = Input.GetAxis("Mouse X") * pSpeed * -0.01f * curDistanceRatio;
                _yFlex = Input.GetAxis("Mouse Y") * pSpeed * -0.01f * curDistanceRatio;
                //_xFlex = Input.GetAxis("Horizontal") * pSpeed * -0.01f * curDistanceRatio;
                //_yFlex = Input.GetAxis("Horizontal") * pSpeed * -0.01f * curDistanceRatio;
                _x = _xFlex;
                _y = _yFlex;
                flexRatioP = 1.0f;
            }
            else if (isFlex)
            {
                _x = _xFlex * flexRatioP;
                _y = _yFlex * flexRatioP;
            }
            else
            {
                _x = 0.0f;
                _y = 0.0f;
            }
            transform.Translate(new Vector3(_x, _y, 0), Space.Self);
            cameraTarget.Translate(new Vector3(_x, _y, 0), transform);

        }

        public float ClampAngle(float angle, float min, float max)
        {
            if (angle < -360)
                angle += 360;
            if (angle > 360)
                angle -= 360;

            return Mathf.Clamp(angle, min, max);
        }
        public void SetFlexRatio(float accTime)
        {
            //旋转
            flexRatio -= Time.deltaTime * 1 / accTime;
            //flexRatio -= Mathf.Sin(Time.deltaTime * 1/accTime * Mathf.PI / 2.0f * Mathf.Rad2Deg);
            flexRatio = Mathf.Clamp01(flexRatio);
            //缩放
            flexRatioZ -= Time.deltaTime * 1 / accTime;
            flexRatioZ = Mathf.Clamp01(flexRatioZ);
            //平移
            flexRatioP -= Time.deltaTime * 1 / accTime;
            flexRatioP = Mathf.Clamp01(flexRatioP);
        }

    }
    /// <summary>
    /// UI拖拽限制类
    /// </summary>
    public class UIDragClamp : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
    {
        [Header("UI限制拖拽位置")]
        public RectTransform container;
    
        RectTransform rt;
        Canvas canvas;
    
        // 位置偏移量
        Vector3 offset = Vector3.zero;
        // 最小、最大X、Y坐标
        float minX, maxX, minY, maxY;
        public float titleHight = 40;

        void Start()
        {
            rt = GetComponent<RectTransform>();

            canvas = GetComponentInParent<Canvas>();
            container = canvas.transform as RectTransform;
        }
    
        bool isHave = false;
    
        public void OnBeginDrag(PointerEventData eventData)
        {
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.enterEventCamera, out Vector3 globalMousePos))
            {
                isHave = false;
                //if (Mathf.Abs(rt.position.y - globalMousePos.y) < titleHight)
               // {
                    // 计算偏移量
                    isHave = true;
               // }
    
                if (isHave)
                {
                    offset = rt.position - globalMousePos;
                    // 设置拖拽范围
                    SetDragRange();
                    this.transform.SetAsLastSibling();
                }
            }
        }
    
        public void OnDrag(PointerEventData eventData)
        {
            // 将屏幕空间上的点转换为位于给定RectTransform平面上的世界空间中的位置
            if (isHave)
            {
                if (RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out Vector3 globalMousePos))
                {
                    rt.position = DragRangeLimit(globalMousePos + offset);
                    
                }
            }
    
        }
        public void OnEndDrag(PointerEventData eventData)
        {
    
        }
        // 通过计算忽略Scale和pivot对UI的影响
        // 设置最大、最小坐标
        void SetDragRange()
        {
            
            if(canvas.renderMode == RenderMode.ScreenSpaceOverlay)
            {
                float canvasScale = canvas.scaleFactor;

            // 获取容器和当前 UI 元素的实际尺寸，考虑自身缩放
                Vector2 containerActualSize = new Vector2(
                    container.sizeDelta.x * container.lossyScale.x,
                    container.sizeDelta.y * container.lossyScale.y
                );
                Vector2 uiActualSize = new Vector2(
                    rt.sizeDelta.x * rt.lossyScale.x,
                    rt.sizeDelta.y * rt.lossyScale.y
                );

                // 获取容器和当前 UI 元素的轴心位置
                Vector2 containerPivot = container.pivot;
                Vector2 uiPivot = rt.pivot;

                // 获取容器的世界坐标
                Vector3 containerWorldPos = container.position;

                // 计算最小和最大 X 坐标
                minX = containerWorldPos.x - (containerActualSize.x * containerPivot.x) / canvasScale + (uiActualSize.x * uiPivot.x) / canvasScale;
                maxX = containerWorldPos.x + (containerActualSize.x * (1 - containerPivot.x)) / canvasScale - (uiActualSize.x * (1 - uiPivot.x)) / canvasScale;

                // 计算最小和最大 Y 坐标
                minY = containerWorldPos.y - (containerActualSize.y * containerPivot.y) / canvasScale + (uiActualSize.y * uiPivot.y) / canvasScale;
                maxY = containerWorldPos.y + (containerActualSize.y * (1 - containerPivot.y)) / canvasScale - (uiActualSize.y * (1 - uiPivot.y)) / canvasScale;

                // 转换到 Canvas 本地空间
                Vector2 containerLocalMin = container.anchoredPosition - new Vector2(containerActualSize.x * containerPivot.x, containerActualSize.y * containerPivot.y) / canvasScale;
                Vector2 containerLocalMax = container.anchoredPosition + new Vector2(containerActualSize.x * (1 - containerPivot.x), containerActualSize.y * (1 - containerPivot.y)) / canvasScale;

                Vector2 uiLocalMin = rt.anchoredPosition - new Vector2(uiActualSize.x * uiPivot.x, uiActualSize.y * uiPivot.y) / canvasScale;
                Vector2 uiLocalMax = rt.anchoredPosition + new Vector2(uiActualSize.x * (1 - uiPivot.x), uiActualSize.y * (1 - uiPivot.y)) / canvasScale;

                minX = containerLocalMin.x + uiActualSize.x * uiPivot.x / canvasScale;
                maxX = containerLocalMax.x - uiActualSize.x * (1 - uiPivot.x) / canvasScale;
                minY = containerLocalMin.y + uiActualSize.y * uiPivot.y / canvasScale;
                maxY = containerLocalMax.y - uiActualSize.y * (1 - uiPivot.y) / canvasScale;
            }
            else
            {

                 container.GetWorldCorners(corners);
                 rt.GetWorldCorners(corners1);

                // 按顺序输出四个角的世界坐标
                minX = corners[0].x;
                maxX = corners[2].x- Vector3.Distance(corners1[0], corners1[3]);
                minY = corners[0].y+ Vector3.Distance(corners1[0], corners1[1]);
                maxY = corners[1].y;

                //  Debug.Log("左下角世界坐标: " + corners1[0]);
                //  Debug.Log("左上角世界坐标: " + corners1[1]);
                //  Debug.Log("右上角世界坐标: " + corners1[2]);
                //  Debug.Log("右下角世界坐标: " + corners1[3]);
            }
            
           
    }
        private Vector3[] corners = new Vector3[4];
        private Vector3[] corners1 = new Vector3[4];
        // 限制坐标范围
        Vector3 DragRangeLimit(Vector3 pos)
        {           
            pos.x = Mathf.Clamp(pos.x, minX, maxX);
            pos.y = Mathf.Clamp(pos.y, minY, maxY);         
            return pos;
        }
    }
}